Skip to content

SubAgents

SubAgents are child agents that can be delegated complex tasks. Unlike AIFunctions (single operations) or Skills (guided workflows), SubAgents have their own reasoning capabilities and can autonomously work through multi-step problems.

What Are SubAgents?

A SubAgent is a fully-configured agent exposed as a tool:

Parent Agent                    SubAgent
┌─────────────────┐            ┌─────────────────┐
│ "Review this PR"│  ────────► │ Code Reviewer   │
│                 │            │                 │
│                 │            │ • Reads files   │
│                 │            │ • Analyzes code │
│                 │  ◄──────── │ • Returns report│
└─────────────────┘            └─────────────────┘
     delegates                    works autonomously

When to use SubAgents:

  • Tasks requiring autonomous multi-step reasoning
  • Specialized domains (code review, research, analysis)
  • Isolating complex workflows from the parent agent

Basic Usage

Mark a method with [SubAgent] and return a SubAgent object:

csharp
public class DelegationTool
{
    [SubAgent]
    public SubAgent CodeReviewer()
    {
        var config = new AgentConfig
        {
            Name = "Code Reviewer",
            SystemInstructions = @"
                You are an expert code reviewer. When given code to review:
                1. Check for bugs and logic errors
                2. Evaluate code style and readability
                3. Suggest improvements
                4. Rate overall quality (1-10)"
        };

        return SubAgentFactory.Create(
            name: "Code Review",
            description: "Reviews code for quality, bugs, and best practices",
            agentConfig: config
        );
    }
}

SubAgentFactory Methods

Create() - Stateless

Each invocation starts fresh with a new conversation thread:

csharp
public static SubAgent Create(
    string name,
    string description,
    AgentConfig agentConfig,
    params Type[] toolTypes  // Optional tools for the SubAgent
)
csharp
[SubAgent]
public SubAgent Researcher()
{
    return SubAgentFactory.Create(
        name: "Research",
        description: "Researches topics thoroughly",
        agentConfig: new AgentConfig { SystemInstructions = "..." }
    );
}

Use when: Each task is independent; no context needs to persist.

CreateStateful() - Shared Thread

Maintains conversation context across multiple invocations:

csharp
public static SubAgent CreateStateful(
    string name,
    string description,
    AgentConfig agentConfig,
    params Type[] toolTypes
)
csharp
[SubAgent]
public SubAgent ProjectAssistant()
{
    return SubAgentFactory.CreateStateful(
        name: "Project Assistant",
        description: "Helps with ongoing project tasks, remembers context",
        agentConfig: new AgentConfig { SystemInstructions = "..." }
    );
}

Use when: The SubAgent needs to remember previous interactions (e.g., ongoing project work).

CreatePerSession() - User-Managed Thread

External thread management for advanced scenarios:

csharp
public static SubAgent CreatePerSession(
    string name,
    string description,
    AgentConfig agentConfig,
    params Type[] toolTypes
)

Use when: You need fine-grained control over thread lifecycle.


Thread Modes Explained

ModeThread BehaviorMemoryUse Case
StatelessNew thread per callNoneIndependent tasks
SharedThreadReuses same threadPersistentOngoing collaboration
PerSessionExternally managedControlledCustom session handling

Example: Stateless vs Stateful

csharp
// Stateless: Each review is independent
[SubAgent]
public SubAgent IndependentReviewer()
{
    return SubAgentFactory.Create(
        name: "Review Code",
        description: "Reviews a single piece of code",
        agentConfig: reviewerConfig
    );
}

// Stateful: Remembers previous reviews, can compare
[SubAgent]
public SubAgent ProjectReviewer()
{
    return SubAgentFactory.CreateStateful(
        name: "Project Reviewer",
        description: "Reviews code with project context, remembers past reviews",
        agentConfig: reviewerConfig
    );
}

SubAgents with Tools

SubAgents can have their own tools:

csharp
[SubAgent]
public SubAgent FileAnalyzer()
{
    var config = new AgentConfig
    {
        Name = "File Analyzer",
        SystemInstructions = "Analyze files and provide insights..."
    };

    return SubAgentFactory.Create(
        name: "Analyze Files",
        description: "Analyzes files for patterns, issues, and insights",
        agentConfig: config,
        typeof(FileSystemTool),    // SubAgent can read files
        typeof(SearchTool)         // SubAgent can search
    );
}

The SubAgent only has access to the tools you explicitly provide.


Provider Inheritance

By default, SubAgents inherit the parent agent's provider (chat client). If you don't specify a Provider in the SubAgent's AgentConfig, it automatically uses the same LLM provider as the parent.

csharp
[SubAgent]
public SubAgent SimpleReviewer()
{
    // No Provider specified = inherits parent's provider
    var config = new AgentConfig
    {
        Name = "Simple Reviewer",
        SystemInstructions = "Review code for issues..."
    };

    return SubAgentFactory.Create(
        name: "Review",
        description: "Quick code review",
        agentConfig: config  // Uses parent's provider automatically
    );
}

This is convenient for most cases—your SubAgents use the same model as the parent without extra configuration.

Overriding the Provider

To use a different provider (e.g., a cheaper model for simple tasks, or a specialized model), explicitly specify Provider in the config:

csharp
[SubAgent]
public SubAgent SpecializedAnalyzer()
{
    var config = new AgentConfig
    {
        Name = "Specialized Analyzer",
        SystemInstructions = "...",
        Provider = new ProviderConfig
        {
            ProviderKey = "openai",
            ModelName = "gpt-4o-mini",  // Use cheaper model for this task
            ApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
        }
    };

    return SubAgentFactory.Create(
        name: "Specialized Analysis",
        description: "Uses GPT-4o-mini for cost-effective analysis",
        agentConfig: config
    );
}

Common patterns:

  • Use a cheaper/faster model for simple SubAgent tasks
  • Use a specialized model (e.g., code-focused) for domain-specific work
  • Use a different provider entirely (e.g., parent uses Anthropic, SubAgent uses OpenAI)

Permission Behavior

SubAgents always require permission by default. This is because they can perform multiple autonomous actions.

Unlike AIFunctions where [RequiresPermission] is opt-in, SubAgents are inherently permission-required:

csharp
// No [RequiresPermission] needed - it's implicit
[SubAgent]
public SubAgent DangerousOperations()
{
    return SubAgentFactory.Create(
        name: "System Admin",
        description: "Performs system administration tasks",
        agentConfig: adminConfig,
        typeof(SystemTool)
    );
}
// User will ALWAYS be prompted before this SubAgent runs

Event Bubbling

SubAgent events bubble up to the parent agent. This allows the parent to:

  • Monitor SubAgent progress
  • Log SubAgent actions
  • React to SubAgent completions
csharp
var agent = new AgentBuilder()
    .WithTool<DelegationTool>()
    .OnToolCall(e => Console.WriteLine($"Tool called: {e.ToolName}"))
    .OnSubAgentStart(e => Console.WriteLine($"SubAgent started: {e.Name}"))
    .OnSubAgentComplete(e => Console.WriteLine($"SubAgent done: {e.Result}"))
    .Build();

Typed Metadata

For compile-time validation, use the generic attribute:

csharp
public class DelegationMetadata : IToolMetadata
{
    public bool HasSpecializedAgents { get; set; }
    // Implementation...
}

public class DelegationTool
{
    [SubAgent<DelegationMetadata>]
    public SubAgent SpecialAgent()
    {
        return SubAgentFactory.Create(
            name: "Special Agent",
            description: "Specialized task handler",
            agentConfig: specialConfig
        );
    }
}

→ See 02.1.4 Tool Metadata.md for details.


Conditional SubAgents

Show or hide SubAgents based on runtime conditions:

csharp
[SubAgent]
[ConditionalSubAgent("HasSpecializedAgents")]
public SubAgent AdvancedAnalyzer()
{
    // Only visible when metadata.HasSpecializedAgents is true
    return SubAgentFactory.Create(
        name: "Advanced Analysis",
        description: "Advanced analysis capabilities",
        agentConfig: advancedConfig
    );
}

→ See 02.1.4 Tool Metadata.md for conditional registration details.


SubAgents vs Skills vs AIFunctions

AspectAIFunctionSkillSubAgent
ComplexitySingle operationMulti-step workflowAutonomous reasoning
ControlDirectGuided by instructionsDelegated
MemoryNoneNoneOptional (stateful)
ToolsParent'sParent's (referenced)Own set
ProviderParent'sParent'sConfigurable
PermissionOpt-inOpt-inAlways required

Decision guide:

  • AIFunction: "Call this function with these parameters"
  • Skill: "Follow these steps using these functions"
  • SubAgent: "Figure out how to accomplish this goal"

Best Practices

  1. Give SubAgents clear, focused purposes: A SubAgent should excel at one domain.

  2. Write detailed system instructions: SubAgents rely heavily on their instructions for autonomous work.

  3. Provide only necessary tools: Don't give SubAgents access to tools they don't need.

  4. Use stateful mode sparingly: Shared threads consume more resources; use only when context is genuinely needed.

  5. Consider provider costs: SubAgents make their own LLM calls; expensive models add up.

csharp
// Good: Focused, well-instructed, minimal tools
[SubAgent]
public SubAgent SecurityAuditor()
{
    var config = new AgentConfig
    {
        Name = "Security Auditor",
        SystemInstructions = @"
            You are a security expert. When auditing code:
            1. Check for OWASP Top 10 vulnerabilities
            2. Identify authentication/authorization issues
            3. Look for data exposure risks
            4. Provide severity ratings (Critical/High/Medium/Low)
            5. Suggest specific fixes for each issue

            Be thorough but focused. Don't report style issues."
    };

    return SubAgentFactory.Create(
        name: "Security Audit",
        description: "Audits code for security vulnerabilities",
        agentConfig: config,
        typeof(FileSystemTool)  // Only needs to read files
    );
}

// Bad: Vague purpose, overpowered
[SubAgent]
public SubAgent DoAnything()
{
    return SubAgentFactory.Create(
        name: "Helper",
        description: "Helps with stuff",
        agentConfig: new AgentConfig { SystemInstructions = "Help the user" },
        typeof(FileSystemTool),
        typeof(DatabaseTool),
        typeof(NetworkTool),
        typeof(SystemTool)  // Way too many capabilities
    );
}

Next Steps

Released under the MIT License.